import type { CheckboxOption } from '../../../../../../components/checkbox/types.ts'
import type {
  INavigatorBarItemConfig,
  IScriptNavigationItem,
} from '../../../../../../components/navigator_bar/types'
import type { OrderType } from '../../../../../../types/interfaces'
import { Checkbox } from '../../../../../../components/checkbox'
import { NavigatorBar } from '../../../../../../components/navigator_bar'
import { BaseModule } from '../../../../../../lib/abstract'
import { ICON_IDS } from '../../../../../../lib/icons/types/constants'
import { PageHandler } from '../../../../../../lib/page_handler'
import { ToolKit } from '../../../../../../lib/utils/toolkit'
import {
  AppStyleCollections,
  BodyAttrs,
  BUILD_CONFIG,
  logger,
  PROJECT_PREFIX,
} from '../../../../../../types/constants'
import { GREASY_FORK_PAGE_ELEMENTS } from '../../../../../../types/constants/personalization/greasy_fork'
import {
  ScriptFeedbackActionType,
  ScriptStatusActionType,
} from '../../../../../../user_center/tab/personalization/custom_css/theme/others/greasy_fork/module/storage_process/types'
import { ConfigManager } from '../../../../../config_manager'
import { ScriptDataMarkerType } from '../../../../data_presentation/platform/greasy_fork/script_valuation/types'
import { badgeConfig, badgeStyleConfigs, baseFields, ControlsType, quickSearchTags, RENDER_CONFIG, sortOptions } from './types'

interface Config {
  enabled: boolean
}

// 测试链接：
// 1）https://greasyfork.org/zh-CN/scripts?q=%E5%81%9A%E4%B8%AA%E7%BB%BF%E8%89%B2%E7%BD%91%E7%AB%99 （我的项目）
// 2）https://greasyfork.org/zh-CN/users/189391-ignore
export class GreasyForkScriptNavigator extends BaseModule {
  id: string = 'greasy_fork_script_navigator'
  name: string = 'GreasyForkScriptNavigator'
  isInitialized: boolean = false
  readonly supportedPlatforms: string[] = ConfigManager.getInstance().moduleSupportedPlatforms(this.name)
  private navigator: NavigatorBar<IScriptNavigationItem> | undefined

  private static instance: GreasyForkScriptNavigator
  private pageHandler: PageHandler = PageHandler.getInstance()

  private scriptListSelector: string = this.pageHandler.services.greasyFork.getScriptListSelector()
  private checkbox: Checkbox | null = null
  private currentIds: number[] | null = null

  defaultConfig: Config = {
    enabled: true,
  }

  private readonly renderConfig: INavigatorBarItemConfig<IScriptNavigationItem>[]
  private controls: Record<ControlsType, boolean> = {
    [ControlsType.onlySearch]: false,
    [ControlsType.hideDisliked]: false,
    [ControlsType.hideFailed]: false,
    [ControlsType.syncSearch]: false,
  }

  private readonly controls_config: CheckboxOption[] = [
    {
      label: '仅搜索',
      value: ControlsType.onlySearch,
      onInit: (checked: boolean) => {
        this.controls.onlySearch = checked
        this.handleOnlySearch()
      },
    },
    {
      label: '自动隐藏不喜欢',
      value: ControlsType.hideDisliked,
      onInit: (checked: boolean) => {
        this.controls.hideDisliked = checked
        this.toggleHideDisliked()
      },
    },
    {
      label: '自动隐藏已失效',
      value: ControlsType.hideFailed,
      onInit: (checked: boolean) => {
        this.controls.hideFailed = checked
        this.toggleHideFailed()
      },
    },
    {
      label: '同步搜索到页面',
      value: ControlsType.syncSearch,
      onInit: (checked: boolean) => {
        this.controls.syncSearch = checked
        this.handleSyncSearch()
      },
    },
  ]

  constructor(config?: Config | object) {
    super()

    this.config = {
      ...this.defaultConfig,
      ...config,
    }

    // 渲染配置
    this.renderConfig = RENDER_CONFIG
  }

  /**
   * 获取单例实例
   */
  public static getInstance(): GreasyForkScriptNavigator {
    if (!GreasyForkScriptNavigator.instance) {
      GreasyForkScriptNavigator.instance = new GreasyForkScriptNavigator()
    }

    return GreasyForkScriptNavigator.instance
  }

  destroy(): void {
    this.navigator?.destroy()
    this.currentIds = null
    this.checkbox?.destroy()
    this.checkbox = null

    super.destroy()
  }

  core(): void {
    this.loadConfig()

    if (!this.config.enabled)
      return

    this.mount()
  }

  onToggleCallback(_id: string, checked: boolean): void {
    super.onToggleCallback(_id, checked)
  }

  applyConfig(config: Partial<Config>): void {
    super.updateConfig(config)
    this.mount()
    logger.info(`[${this.name}] Config updated and apply`, this.config)
  }

  /**
   * 加载导航器
   */
  private loadNavigator(): void {
    this.navigator = new NavigatorBar<IScriptNavigationItem>({
      id: AppStyleCollections.Module.SCRIPT_NAVIGATOR.id,
      layout: {
        width: AppStyleCollections.Module.SCRIPT_NAVIGATOR.width,
        heightOptions: {
          base: AppStyleCollections.Module.SCRIPT_NAVIGATOR.height,
          max: AppStyleCollections.Module.SCRIPT_NAVIGATOR.height,
        },
        transform: AppStyleCollections.Module.SCRIPT_NAVIGATOR.transform,
      },
      display: {
        itemConfig: this.renderConfig,
        baseFields,
        badgeConfig,
        badgeStyleConfigs,
      },
      behavior: {
        handlers: {
          matchItem: this.matchItem.bind(this),
          sortItems: this.sortItems.bind(this),
        },
        afterRenderList: (ids: number[]) => {
          this.currentIds = ids
          this.applyControlHide()
          this.handleListChange(ids)
        },
        preventSearchClear: (): boolean => this.toggleSearchClear(),
      },
      styles: this.getCustomStyles(),
      currentItemSelectorById: (id: number) => `.list .item[data-id="${id}"]`,
      currentItemSelectorByIndex: (index: number) => `.list .item[data-index="${index}"]`,
      scrollSelector: {
        element: this.scriptListSelector,
        item: (id: number) => ToolKit.ui.element.concatAttributeSelector(this.scriptListSelector.trim(), 'data-script-id', id),
      },
      placeholder: '搜索脚本，试试输入「热门」查看内容...',
      quickSearchTags,
      sortOptions,
      sortOrder: 'desc',
      sortType: 'score',
      keepDropdownOpen: () => this.controls.onlySearch,
    })

    this.navigator.mount()
  }

  /**
   * 挂载组件
   */
  mount(): void {
    this.loadNavigator()
    this.bindEvents()
    this.addControls()
  }

  /**
   * 绑定事件
   */
  private bindEvents(): void {
    this.updateNavigatorList()
    this.handleLinkClick()
  }

  /*
   * 更新导航器选项
   */
  private updateNavigatorList(): void {
    const $scriptList = $(this.scriptListSelector)

    const items: IScriptNavigationItem[] = !$scriptList || !$scriptList.length
      ? []
      : $scriptList
          .map((_i, el) => {
            const $item = $(el)

            // 提取标签数据
            const tags: string[] = []
            $item.find('.script-tag').each((_, tagEl) => {
              const type = $(tagEl).data('type')
              tags.push(type)
            })
            // 提取数据标记
            $item.filter(Object.values(ScriptDataMarkerType).map(type => `[${type}]`).join(', ')).each((_, tagEl) => {
              if ($(tagEl).attr(ScriptDataMarkerType.MarkerLiker) === ScriptFeedbackActionType.Like) {
                tags.push('like')
              }
              if ($(tagEl).attr(ScriptDataMarkerType.MarkerLiker) === ScriptFeedbackActionType.Dislike) {
                tags.push('dislike')
              }
              if ($(tagEl).attr(ScriptDataMarkerType.MarkerSuccess) === ScriptStatusActionType.MarkSuccess) {
                tags.push('success')
              }
              if ($(tagEl).attr(ScriptDataMarkerType.MarkerSuccess) === ScriptStatusActionType.MarkFailed) {
                tags.push('failed')
              }
            })

            return {
              id: Number.parseInt($item.attr('data-script-id') || '0'),
              name: $item.find('a.script-link').text().trim(),
              author: $item.find('dd.script-list-author a').text().trim(),
              authorUrl: $item.find('dd.script-list-author a').attr('href') || '',
              avatar: BUILD_CONFIG.IMAGE.DEFAULT_AVATAR_URL,
              description: $item.find('.script-description').text().trim(),
              score: Number.parseInt($item.find('.script-meta-container .score-value').text().trim() || '59'),
              totalInstalls: Number.parseInt($item.attr('data-script-total-installs') || '0'),
              dailyInstalls: Number.parseInt($item.attr('data-script-daily-installs') || '0'),
              version: $item.attr('data-script-version') || '',
              url: $item.attr('data-code-url') || '',
              goodRating: Number.parseInt($item.find('.good-rating-count').text().trim() || '0'),
              badRating: Number.parseInt($item.find('.bad-rating-count').text().trim() || '0'),
              updateTime: $item.attr('data-script-updated-date') || '0',
              createdDate: $item.attr('data-script-created-date') || '0',
              tags,
              // 【必须！！！】为每个标签类型添加单独字段
              ...quickSearchTags.reduce((acc, tag) => {
                acc[`tag-${tag.type}`] = tags.includes(tag.type) ? tag.label : ''
                return acc
              }, {} as Record<string, string>),
            } as IScriptNavigationItem
          })
          .get()

    this.navigator?.updateOptions(items)

    // 标记为项目元素
    ToolKit.browser.platform.markProjectElement(this.navigator?.$element)
  }

  /**
   * 处理链接点击事件
   */
  private handleLinkClick(): void {
    this.navigator?.$element?.find('a.author, a.script-id').on('click', (e) => {
      e.preventDefault()
      e.stopPropagation()

      const $item = $(e.target)
      const href = $item.attr('href') || ''

      const isExternal = ToolKit.browser.url.isExternalLink(href)

      ToolKit.browser.url.openNewTab(href, isExternal ? 'GM' : 'Browser')
    })
  }

  /**
   * 处理列表变化事件
   * @param ids 脚本 ID 列表
   */
  handleListChange(ids: number[]): void {
    if (this.controls.syncSearch) {
      this.handleListVisibility(ids)
    }
  }

  handleListVisibility(ids?: number[]): void {
    const $element = $('#browse-script-list li')
    // 未指定 id 时，目标元素取非通过搜索隐藏的其他元素
    const $target = ids ? $element : $element.not(`[${BodyAttrs.HIDE_TYPE}="search"]`)
    $target.each((_i, el) => {
      const $item = $(el)
      const itemId = $item.attr('data-script-id') || ''
      const hideIds = this.getControlHideIds()
      const isHide = hideIds.includes(Number(itemId))
      const show = (!ids || ids.includes(Number(itemId))) && !isHide
      $item.toggleClass(AppStyleCollections.Hide.ELEMENT, !show)
      if (show) {
        $item.show()
        $item.removeAttr(BodyAttrs.HIDE_TYPE)
      }
      else {
        $item.hide()
        $item.attr(BodyAttrs.HIDE_TYPE, isHide ? 'hide' : 'search')
      }
    })
  }

  /**
   * 切换搜索清除按钮
   * @returns 是否清除搜索
   */
  toggleSearchClear(): boolean {
    return !this.controls.syncSearch
  }

  /**
   * 添加控制项到导航器
   */
  addControls(): void {
    const $container = this.navigator?.$element?.find('.dropdown')

    // 创建实例
    this.checkbox = new Checkbox({
      id: 'script-controls',
      options: this.controls_config,
      onChange: (value: string, isChecked: boolean) => this.handleControlChange(value as ControlsType, isChecked),
    })

    $container?.prepend(this.checkbox.render())

    if (!$container || !$container.length)
      return

    this.checkbox?.mount($container)
  }

  /**
   * 处理控制项变化
   * @param action 动作名称
   * @param isChecked 是否选中
   */
  handleControlChange(action: ControlsType, isChecked: boolean): void {
    this.controls[action] = isChecked

    switch (action) {
      case ControlsType.onlySearch:
        this.handleOnlySearch()
        break
      case ControlsType.hideDisliked:
        this.toggleHideDisliked()
        this.handleListVisibility()
        break
      case ControlsType.hideFailed:
        this.toggleHideFailed()
        this.handleListVisibility()
        break
      case ControlsType.syncSearch:
        this.handleSyncSearch()
        break
    }
  }

  getNavigatorItem(): JQuery {
    return this.navigator?.$element?.find('.dropdown .item') || $()
  }

  handleOnlySearch(): void {
    this.navigator?.$element
      ?.find('.current-item, .dropdown .list, .sort-box')
      .toggleClass(AppStyleCollections.Hide.ELEMENT, this.controls.onlySearch)

    const selector = GREASY_FORK_PAGE_ELEMENTS.CONTENT_CONTAINER.selector
    if (this.controls.onlySearch) {
      const dropdownHeight = this.navigator?.$element?.find('.dropdown')[0]?.getBoundingClientRect().height
      if (dropdownHeight) {
        $(selector)[0].style.setProperty('margin-top', `${dropdownHeight}px`, 'important')
      }
    }
    else {
      $(selector).css('margin-top', '')
    }
  }

  toggleHideDisliked(): void {
    this.getNavigatorItem()
      .has('.tags .tag-dislike')
      .toggleClass(AppStyleCollections.Hide.ELEMENT, this.controls.hideDisliked)
  }

  toggleHideFailed(): void {
    this.getNavigatorItem()
      .has('.tags .tag-failed')
      .toggleClass(AppStyleCollections.Hide.ELEMENT, this.controls.hideFailed)
  }

  handleSyncSearch(): void {
    const $element = $('#browse-script-list li')

    if (this.controls.syncSearch && this.currentIds) {
      this.handleListVisibility(this.currentIds)
    }
    else {
      $element.removeClass(AppStyleCollections.Hide.ELEMENT).removeAttr(BodyAttrs.HIDE_TYPE).show()
    }
  }

  applyControlHide(): void {
    this.toggleHideDisliked()
    this.toggleHideFailed()
  }

  getControlHideIds(): number[] {
    return this.getNavigatorItem()
      .filter(`.${AppStyleCollections.Hide.ELEMENT}`)
      .map((_i, el) => {
        const id = $(el).data('id')
        return typeof id === 'number'
          ? id
          : Number(id) || 0
      })
      .get()
      .filter(Number.isInteger)
  }

  // 匹配逻辑
  private matchItem(item: IScriptNavigationItem, query: string, isQuickTagSearch: boolean): boolean {
    // 快捷标签搜索时精确匹配标签类型
    if (isQuickTagSearch) {
      // 先检查是否匹配标签类型或标签文字
      const isTagMatch = item.tags?.some(tag =>
        tag.toLowerCase() === query.toLowerCase()
        || quickSearchTags.some(qTag =>
          qTag.type === tag && qTag.label.toLowerCase() === query.toLowerCase(),
        ),
      )

      // 如果标签不匹配，再检查条件匹配
      if (!isTagMatch) {
        const matchedTag = quickSearchTags.find(tag =>
          tag.type.toLowerCase() === query.toLowerCase()
          || tag.label.toLowerCase() === query.toLowerCase(),
        )
        return matchedTag?.matchCondition ? matchedTag.matchCondition(item) : false
      }
      return true
    }

    // 普通搜索时匹配所有相关字段
    const searchContent = [
      item.name,
      item.author,
      item.description,
      ...(item.tags || []).map(tag =>
        quickSearchTags.find(t => t.type === tag)?.label || tag,
      ),
    ]
      .filter(Boolean)
      .join(' ')
      .toLowerCase()

    return searchContent.includes(query.toLowerCase())
  }

  // 排序逻辑
  private sortItems(items: IScriptNavigationItem[], sortType: string, sortOrder: OrderType): IScriptNavigationItem[] {
    return items.sort((a, b) => {
      let valueA, valueB

      switch (sortType) {
        case 'score':
          valueA = a.score || 0
          valueB = b.score || 0
          break
        case 'daily-installs':
          valueA = a.dailyInstalls || 0
          valueB = b.dailyInstalls || 0
          break
        case 'total-installs':
          valueA = a.totalInstalls || 0
          valueB = b.totalInstalls || 0
          break
        case 'good-rating':
          valueA = a.goodRating || 0
          valueB = b.goodRating || 0
          break
        case 'bad-rating':
          valueA = a.badRating || 0
          valueB = b.badRating || 0
          break
        case 'updated':
          valueA = new Date(a.updateTime || 0).getTime()
          valueB = new Date(b.updateTime || 0).getTime()
          break
        default:
          valueA = a.score || 0
          valueB = b.score || 0
      }

      return sortOrder === 'asc' ? valueA - valueB : valueB - valueA
    })
  }

  refresh(): void {
    this.updateNavigatorList()
  }

  private getCustomStyles(): string {
    return `
.current-item :is(.name, .description) {
  ${this.stylesService.wordBreak.multiLine()}
}

.custom-checkbox {
  padding: 8px 12px;
}

/* 元数据 */
.meta .header {
  gap: 20px;    
  > span::before, > a::before {
    ${this.stylesService.inlinePseudo.icon()}
  }

  .author {
    font-weight: 600;
    color: var(--${PROJECT_PREFIX}-theme-text-primary) !important;
    white-space: nowrap;
  }
  .author::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.role.developer)}) !important;
  }
  
  .script-id {
    white-space: nowrap;
  }
  .script-id::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.metadata.id)}) !important;
  }
}

/* 属性 */
.meta .stats {
  > span::before  {
    ${this.stylesService.inlinePseudo.icon({ marginRight: 2 })}
  }
  
  .index::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.metadata.index)}) !important;
  }
  
  .score::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.other.score)}) !important;
  }
  
  .daily-installs::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.action.download)}) !important;
  }
  
  .total-installs::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.action.download)}) !important;
  }
  
  .good-rating::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.rating.good)}) !important;
  }
  
  .bad-rating::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.rating.bad)}) !important;
  }
  
  .update-time::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.development.update)}) !important;
  }
  
  .version::before {
    background-image: var(${this.themeVarService.generateIcon(ICON_IDS.development.version)}) !important;
  }
}
`
  }
}
